﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CloudShot.Controls;
using CloudShot.Editor;
using CloudShot.Interfaces;
using CloudShot.Properties;
using CloudShot.Utils;
using CloudShot.Utils.Drawings;
using CloudShot.ViewModels;
using Custom;

namespace CloudShot.Forms
{
  public sealed partial class PropertiesForm : MoveableForm
  {
    private const int WaitInterval = 500;
    private const int TickInterval = 40;
    private static readonly Color _backColor = Color.FromArgb(255, 221, 111);
    private static readonly Color _borderColor = Color.FromArgb(194, 148, 60);

    private readonly IDrawingCanvas _canvas;
    private readonly FigurePropertiesControl _figurePropertiesControl;
    private readonly ToolStripDropDown _figurePropertiesDropdown = new ToolStripDropDown();
    private readonly List<IPropertiesControlGroup> _controlGroups = new List<IPropertiesControlGroup>();
    private readonly List<IPropertyHandler> _propertyHandlers = new List<IPropertyHandler>();

    private readonly Timer _textSizeTimer = new Timer();
    private readonly IFormHotkeyService _formHotkeyService;

    private readonly ButtonAppearanceTheme _buttonAppearanceTheme = new ButtonAppearanceTheme
    {
      BorderColor = _borderColor,
      BorderSize = 0,
      CheckedBackColor = _backColor,
      MouseOverBackColor = _backColor,
      MouseDownBackColor = _backColor
    };

    private int _timerIndexStep;
    private bool _suppressEvents;

    private PropertiesFormViewModel _viewModel;

    public event EventHandler ReplaceWithTextRequest;

    public PropertiesForm(IDrawingCanvas canvas, EditorTheme theme, IFormHotkeyService formHotkeyService)
    {
      _canvas = canvas;
      _formHotkeyService = formHotkeyService;

      FormBorderStyle = FormBorderStyle.None;
      DoubleBuffered = true;
      SetStyle(ControlStyles.ResizeRedraw, true);

      InitializeComponent();

      _figurePropertiesControl = new FigurePropertiesControl(_canvas, theme)
      {
        Margin = Padding.Empty,
        Padding = Padding.Empty
      };

      _textSizeTimer.Tick += OnTimerTick;
      _textSizeTimer.Interval = 300;

      DragControl = captionLabel;

      _figurePropertiesDropdown.Items.Add(_figurePropertiesControl.AsToolStripControlHost());
      
      _figurePropertiesControl.CloseRequest += OnCloseRequest;

      var textAlignmentGroup = new RadioButtonPropertyHandler<StringAlignment?>(m => m.TextAlignment, false);
      textAlignmentGroup.Add(rdoTextLeft, StringAlignment.Near);
      textAlignmentGroup.Add(rdoTextCenter, StringAlignment.Center);
      textAlignmentGroup.Add(rdoTextRight, StringAlignment.Far);

      fontSizeComboBox.Items.AddRange(theme.FontSizes.Cast<object>().ToArray());
      fontSizeComboBox.SelectedIndexChanged += OnFontSizeChanged;
      fontSizeComboBox.DropDownClosed += OnFontSizeChanged;
      fontSizeComboBox.Hover += OnHoverItem;

      _controlGroups.AddRange(new IPropertiesControlGroup[]
      {
        textAlignmentGroup
      });

      _propertyHandlers.AddRange(new IPropertyHandler[]
      {
        new CheckBoxPropertyHandler<bool?>(m => m.IsBold, chkBold, true, false, hasPreviewByHover: false),
        new CheckBoxPropertyHandler<bool?>(m => m.IsItalic, chkItalic, true, false, hasPreviewByHover: false),
        new CheckBoxPropertyHandler<bool?>(m => m.IsUnderline, chkUnderline, true, false, hasPreviewByHover: false),
        new CheckBoxPropertyHandler<bool?>(m => m.IsArrowAtEnd, chkArrowRight, true, false),
        new CheckBoxPropertyHandler<bool?>(m => m.IsArrowAtStart, chkArrowLeft, true, false)
      });

      foreach (IPropertiesControlGroup propertiesControlGroup in _controlGroups)
      {
        propertiesControlGroup.CloseRequest += OnRefreshCanvas;
        propertiesControlGroup.PreviewRequest += OnRefreshCanvas;
      }

      foreach (IPropertyHandler propertyHandler in _propertyHandlers)
      {
        propertyHandler.RefreshRequest += OnRefreshCanvas;
      }

      _buttonAppearanceTheme.Ignore.Add(addTextButton);
      _buttonAppearanceTheme.ApplyTo(mainPanel);
      _buttonAppearanceTheme.ApplyTo(_figurePropertiesControl);
    }

    internal void SetViewModel(PropertiesFormViewModel viewModel)
    {
      _viewModel = viewModel;

      if (_viewModel != null)
      {
        btnIncreaseText.Enabled = btnDecreaseText.Enabled = viewModel.CanChangeFontSizeByButtons;
        fontSizeComboBox.SelectedItem = viewModel.FontSize;
      }

      _figurePropertiesControl.SetViewModel(viewModel);
      ApplyViewModel(viewModel);
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
      if (_formHotkeyService.ProcessKey(new KeyEventArgs(keyData)))
        return true;

      return base.ProcessCmdKey(ref msg, keyData);
    }

    private void StartTimer(bool increase)
    {
      _timerIndexStep = increase ? 1 : -1;
      _textSizeTimer.Interval = WaitInterval;
      _textSizeTimer.Enabled = true;
      _textSizeTimer.Start();
    }

    private void ResizeText()
    {
      ChangeTextSize(_timerIndexStep);
    }

    private void OnHoverItem(object sender, HoverEventArgs e)
    {
      SetTextSizeAndRedraw((float)fontSizeComboBox.Items[e.ItemIndex]);
    }

    private void OnFontSizeChanged(object sender, EventArgs e)
    {
      if (_viewModel != null)
      {
        SetTextSizeAndRedraw((float?)fontSizeComboBox.SelectedItem);
      }
    }

    private void ChangeTextSize(int indexStep)
    {
      if (_suppressEvents)
        return;

      if (_viewModel.FontSize == null)
        return;

      int index = fontSizeComboBox.SelectedIndex + indexStep;

      if (index >= fontSizeComboBox.Items.Count)
        return;

      if (index < 0)
        return;

      fontSizeComboBox.SelectedIndex = index;//SelectedIndexChanged event will be invoked
    }

    private void SetTextSizeAndRedraw(float? newFontSize)
    {
      _viewModel.FontSize = newFontSize;
      _canvas.RefreshCanvas(_viewModel.GetBoundingBox());
    }

    private void ShowPropertiesDropdown(Button button, FigurePropertiesControl.VisiblePropertiesState visibleProperties)
    {
      if (button == null)
        return;

      _figurePropertiesControl.VisibleProperties = visibleProperties;
      _figurePropertiesDropdown.Show(button, new Point(0, button.Height));
    }

    private void UpdateButtonColors(PropertiesFormViewModel viewModel)
    {
      if (viewModel == null)
        return;

      Color nullColor = Color.Snow;

      backColorButton.Image = Resources.properties_background_color.ReplaceColor(Color.Magenta, viewModel.BackColor ?? nullColor);
      borderColorButton.Image = Resources.properties_color.ReplaceColor(Color.Magenta, viewModel.BorderColor ?? nullColor);
      textColorButton.Image = Resources.properties_font_color.ReplaceColor(Color.Magenta, viewModel.TextColor ?? nullColor);
    }

    private void InvokeReplaceWithTextRequest()
    {
      EventHandler handler = ReplaceWithTextRequest;
      if (handler != null)
        handler(this, EventArgs.Empty);
    }

    #region Initialization

    private void ApplyViewModel(PropertiesFormViewModel viewModel)
    {
      _suppressEvents = true;
      mainPanel.SuspendLayout();
      try
      {
        foreach (IPropertiesControlGroup propertiesControlGroup in _controlGroups)
        {
          propertiesControlGroup.SetViewModel(viewModel);
        }

        foreach (IPropertyHandler propertyHandler in _propertyHandlers)
        {
          propertyHandler.SetViewModel(viewModel);
        }

        mainPanel.Visible = viewModel != null;

        if (viewModel == null)
          return;

        Text = viewModel.Name;

        arrowPanel.Visible = viewModel.IsArrowPartVisible;
        textToolsPanel.Visible = viewModel.IsTextPartVisible;
        backColorButton.Visible = viewModel.IsBackColorPartVisible;
        addTextButton.Visible = viewModel.IsAddTextPartVisible;

        layersPanel.FlowDirection = viewModel.IsTextPartVisible || viewModel.IsAddTextPartVisible ? FlowDirection.TopDown : FlowDirection.LeftToRight;
        toolsPanel.FlowDirection = viewModel.IsAddTextPartVisible || viewModel.IsArrowPartVisible || !viewModel.IsTextPartVisible
          ? FlowDirection.LeftToRight
          : FlowDirection.TopDown;

        UpdateButtonColors(_viewModel);
      }
      finally
      {
        mainPanel.ResumeLayout(true);
        _suppressEvents = false;
      }
    }

    #endregion

    #region Event Handlers

    private void OnRefreshCanvas(object sender, EventArgs e)
    {
      _canvas.RefreshCanvas(((PropertiesFormViewModel)sender).GetBoundingBox());
    }

    private void OnRefreshCanvas(PropertiesFormViewModel model)
    {
      _canvas.RefreshCanvas(model.GetBoundingBox());
    }

    private void OnSendToBack(object sender, EventArgs e)
    {
      _canvas.GraphicsList.MoveSelectionToBack();
      _canvas.RefreshCanvas();
    }

    private void OnBringToFront(object sender, EventArgs e)
    {
      _canvas.GraphicsList.MoveSelectionToFront();
      _canvas.RefreshCanvas();
    }

    private void OnIncreaseFontMouseDown(object sender, MouseEventArgs e)
    {
      StartTimer(true);
    }

    private void OnDecreaseFontMouseDown(object sender, MouseEventArgs e)
    {
      StartTimer(false);
    }

    private void OnResizeFontMouseUp(object sender, MouseEventArgs e)
    {
      _textSizeTimer.Enabled = false;

      if (_textSizeTimer.Interval == WaitInterval)
      {
        //means simple click
        ResizeText();
      }
    }

    private void OnTimerTick(object sender, EventArgs e)
    {
      _textSizeTimer.Interval = TickInterval;
      ResizeText();
    }

    private void OnFormTextChanged(object sender, EventArgs e)
    {
      captionLabel.Text = Text;
    }

    private void OnBackgroundButtonClick(object sender, EventArgs e)
    {
      ShowPropertiesDropdown(sender as Button, FigurePropertiesControl.VisiblePropertiesState.Background);
    }

    private void OnBorderButtonClick(object sender, EventArgs e)
    {
      ShowPropertiesDropdown(sender as Button, FigurePropertiesControl.VisiblePropertiesState.Border);
    }

    private void OnTextButtonClick(object sender, EventArgs e)
    {
      ShowPropertiesDropdown(sender as Button, FigurePropertiesControl.VisiblePropertiesState.Text);
    }

    private void OnAddTextClick(object sender, EventArgs e)
    {
      InvokeReplaceWithTextRequest();
    }

    private void OnCloseRequest(object sender, EventArgs e)
    {
      _figurePropertiesDropdown.Close();
      UpdateButtonColors(_viewModel);
    }

    #endregion
  }
}